"""
离线语言播放系统 Python SDK
"""

import asyncio
import json
import aiohttp
import base64
import os
from typing import Optional, Dict, Any, List, Union
from dataclasses import dataclass, asdict
from datetime import datetime
import websockets
from pathlib import Path


@dataclass
class TTSRequest:
    """TTS请求"""
    text: str
    voice_id: Optional[str] = None
    language: Optional[str] = None
    voice_settings: Optional[Dict[str, float]] = None
    output_format: Optional[str] = None
    sample_rate: Optional[int] = None
    channels: Optional[int] = None


@dataclass
class TTSResult:
    """TTS结果"""
    success: bool
    audio_url: Optional[str] = None
    download_url: Optional[str] = None
    duration: Optional[float] = None
    file_size: Optional[int] = None
    format: Optional[str] = None
    sample_rate: Optional[int] = None
    channels: Optional[int] = None
    bitrate: Optional[int] = None
    error: Optional[str] = None


@dataclass
class Voice:
    """语音信息"""
    id: str
    name: str
    language: str
    gender: str
    engine: str
    age_range: Optional[str] = None
    description: Optional[str] = None
    sample_rate: Optional[int] = None
    supported_formats: Optional[List[str]] = None


@dataclass
class AudioBookRequest:
    """有声读物创建请求"""
    title: str
    content: str
    author: Optional[str] = None
    description: Optional[str] = None
    language: Optional[str] = None
    voice_profile: Optional[Dict[str, Any]] = None
    auto_chapters: Optional[bool] = None
    chapter_separator: Optional[str] = None


@dataclass
class AudioBook:
    """有声读物"""
    id: str
    title: str
    author: Optional[str] = None
    description: Optional[str] = None
    language: Optional[str] = None
    status: Optional[str] = None
    duration: Optional[float] = None
    chapters_count: Optional[int] = None
    file_path: Optional[str] = None
    file_size: Optional[int] = None
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None


@dataclass
class Chapter:
    """章节"""
    id: str
    title: str
    order: int
    duration: Optional[float] = None
    file_path: Optional[str] = None
    file_size: Optional[int] = None
    status: Optional[str] = None
    created_at: Optional[datetime] = None


@dataclass
class PlaySession:
    """播放会话"""
    session_id: str
    current_chapter: Optional[int] = None
    current_position: Optional[float] = None
    stream_url: Optional[str] = None
    websocket_url: Optional[str] = None


@dataclass
class Progress:
    """播放进度"""
    current_chapter: int
    current_position: float
    total_progress: float
    total_read_time: Optional[float] = None
    last_read_at: Optional[datetime] = None


class OfflineLanguagePlayerError(Exception):
    """SDK基础异常"""
    def __init__(self, message: str, code: str = None, details: Dict[str, Any] = None):
        self.message = message
        self.code = code
        self.details = details or {}
        super().__init__(message)


class ValidationError(OfflineLanguagePlayerError):
    """验证错误"""
    pass


class AuthenticationError(OfflineLanguagePlayerError):
    """认证错误"""
    pass


class RateLimitError(OfflineLanguagePlayerError):
    """速率限制错误"""
    pass


class APIError(OfflineLanguagePlayerError):
    """API错误"""
    pass


class TTSModule:
    """TTS模块"""
    
    def __init__(self, client):
        self.client = client
    
    async def synthesize(self, request: TTSRequest) -> TTSResult:
        """语音合成"""
        try:
            data = {
                "text": request.text,
                **({} if not request.voice_id else {"voice_id": request.voice_id}),
                **({} if not request.language else {"language": request.language}),
                **({} if not request.voice_settings else {"voice_settings": request.voice_settings}),
                **({} if not request.output_format else {"output_format": request.output_format}),
                **({} if not request.sample_rate else {"sample_rate": request.sample_rate}),
                **({} if not request.channels else {"channels": request.channels}),
            }
            
            response = await self.client._post("/api/v1/tts/synthesize", data)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            data = response["data"]
            return TTSResult(
                success=True,
                audio_url=data.get("audio_url"),
                download_url=data.get("download_url"),
                duration=data.get("duration"),
                file_size=data.get("file_size"),
                format=data.get("format"),
                sample_rate=data.get("sample_rate"),
                channels=data.get("channels"),
                bitrate=data.get("bitrate")
            )
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"语音合成失败: {str(e)}")
    
    async def batch_synthesize(
        self,
        texts: List[str],
        voice_id: Optional[str] = None,
        language: Optional[str] = None,
        voice_settings: Optional[Dict[str, float]] = None,
        output_format: Optional[str] = None
    ) -> Dict[str, Any]:
        """批量语音合成"""
        try:
            data = {
                "texts": texts,
                **({} if not voice_id else {"voice_id": voice_id}),
                **({} if not language else {"language": language}),
                **({} if not voice_settings else {"voice_settings": voice_settings}),
                **({} if not output_format else {"output_format": output_format}),
            }
            
            response = await self.client._post("/api/v1/tts/batch-synthesize", data)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            return response["data"]
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"批量语音合成失败: {str(e)}")
    
    async def get_voices(
        self,
        language: Optional[str] = None,
        gender: Optional[str] = None,
        engine: Optional[str] = None
    ) -> List[Voice]:
        """获取可用语音"""
        try:
            params = {}
            if language:
                params["language"] = language
            if gender:
                params["gender"] = gender
            if engine:
                params["engine"] = engine
            
            response = await self.client._get("/api/v1/tts/voices", params)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            voices = []
            for voice_data in response["data"]:
                voices.append(Voice(**voice_data))
            
            return voices
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"获取语音列表失败: {str(e)}")
    
    async def download_audio(self, audio_url: str, save_path: str) -> bool:
        """下载音频文件"""
        try:
            full_url = f"{self.client.base_url}{audio_url}"
            
            async with self.client.session.get(full_url) as response:
                if response.status == 200:
                    content = await response.read()
                    
                    # 确保目录存在
                    Path(save_path).parent.mkdir(parents=True, exist_ok=True)
                    
                    with open(save_path, 'wb') as f:
                        f.write(content)
                    
                    return True
                else:
                    raise APIError(f"下载失败，状态码: {response.status}")
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"下载音频失败: {str(e)}")


class AudioBookModule:
    """有声读物模块"""
    
    def __init__(self, client):
        self.client = client
    
    async def create(self, request: AudioBookRequest) -> Dict[str, Any]:
        """创建有声读物"""
        try:
            data = asdict(request)
            
            response = await self.client._post("/api/v1/audiobooks", data)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            return response["data"]
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"创建有声读物失败: {str(e)}")
    
    async def list(
        self,
        page: int = 1,
        size: int = 20,
        status: Optional[str] = None,
        language: Optional[str] = None,
        search: Optional[str] = None,
        sort_by: str = "created_at",
        sort_order: str = "desc"
    ) -> Dict[str, Any]:
        """获取有声读物列表"""
        try:
            params = {
                "page": page,
                "size": size,
                "sort_by": sort_by,
                "sort_order": sort_order
            }
            
            if status:
                params["status"] = status
            if language:
                params["language"] = language
            if search:
                params["search"] = search
            
            response = await self.client._get("/api/v1/audiobooks", params)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            # 转换为AudioBook对象
            audiobooks = []
            for item in response["data"]["items"]:
                item["created_at"] = datetime.fromisoformat(item["created_at"]) if item.get("created_at") else None
                item["updated_at"] = datetime.fromisoformat(item["updated_at"]) if item.get("updated_at") else None
                audiobooks.append(AudioBook(**item))
            
            return {
                "items": audiobooks,
                "pagination": response["data"]["pagination"]
            }
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"获取有声读物列表失败: {str(e)}")
    
    async def get(self, audiobook_id: str) -> AudioBook:
        """获取有声读物详情"""
        try:
            response = await self.client._get(f"/api/v1/audiobooks/{audiobook_id}")
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            data = response["data"]
            data["created_at"] = datetime.fromisoformat(data["created_at"]) if data.get("created_at") else None
            data["updated_at"] = datetime.fromisoformat(data["updated_at"]) if data.get("updated_at") else None
            
            return AudioBook(**data)
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"获取有声读物详情失败: {str(e)}")
    
    async def update(self, audiobook_id: str, **kwargs) -> AudioBook:
        """更新有声读物"""
        try:
            response = await self.client._put(f"/api/v1/audiobooks/{audiobook_id}", kwargs)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            data = response["data"]
            data["created_at"] = datetime.fromisoformat(data["created_at"]) if data.get("created_at") else None
            data["updated_at"] = datetime.fromisoformat(data["updated_at"]) if data.get("updated_at") else None
            
            return AudioBook(**data)
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"更新有声读物失败: {str(e)}")
    
    async def delete(self, audiobook_id: str) -> bool:
        """删除有声读物"""
        try:
            response = await self.client._delete(f"/api/v1/audiobooks/{audiobook_id}")
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            return True
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"删除有声读物失败: {str(e)}")
    
    async def get_chapters(self, audiobook_id: str) -> List[Chapter]:
        """获取章节列表"""
        try:
            response = await self.client._get(f"/api/v1/audiobooks/{audiobook_id}/chapters")
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            chapters = []
            for chapter_data in response["data"]:
                chapter_data["created_at"] = datetime.fromisoformat(chapter_data["created_at"]) if chapter_data.get("created_at") else None
                chapters.append(Chapter(**chapter_data))
            
            return chapters
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"获取章节列表失败: {str(e)}")
    
    async def play(
        self,
        audiobook_id: str,
        chapter_index: Optional[int] = None,
        position: Optional[float] = None
    ) -> PlaySession:
        """开始播放"""
        try:
            data = {}
            if chapter_index is not None:
                data["chapter_index"] = chapter_index
            if position is not None:
                data["position"] = position
            
            response = await self.client._post(f"/api/v1/audiobooks/{audiobook_id}/play", data)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            data = response["data"]
            return PlaySession(**data)
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"开始播放失败: {str(e)}")
    
    async def pause(self, audiobook_id: str) -> bool:
        """暂停播放"""
        try:
            response = await self.client._post(f"/api/v1/audiobooks/{audiobook_id}/pause")
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            return True
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"暂停播放失败: {str(e)}")
    
    async def resume(self, audiobook_id: str) -> bool:
        """恢复播放"""
        try:
            response = await self.client._post(f"/api/v1/audiobooks/{audiobook_id}/resume")
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            return True
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"恢复播放失败: {str(e)}")
    
    async def stop(self, audiobook_id: str) -> bool:
        """停止播放"""
        try:
            response = await self.client._post(f"/api/v1/audiobooks/{audiobook_id}/stop")
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            return True
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"停止播放失败: {str(e)}")
    
    async def seek(
        self,
        audiobook_id: str,
        chapter_index: Optional[int] = None,
        position: float = 0.0
    ) -> bool:
        """跳转位置"""
        try:
            data = {"position": position}
            if chapter_index is not None:
                data["chapter_index"] = chapter_index
            
            response = await self.client._post(f"/api/v1/audiobooks/{audiobook_id}/seek", data)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            return True
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"跳转位置失败: {str(e)}")
    
    async def get_progress(self, audiobook_id: str) -> Progress:
        """获取播放进度"""
        try:
            response = await self.client._get(f"/api/v1/audiobooks/{audiobook_id}/progress")
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            data = response["data"]
            data["last_read_at"] = datetime.fromisoformat(data["last_read_at"]) if data.get("last_read_at") else None
            
            return Progress(**data)
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"获取播放进度失败: {str(e)}")
    
    async def update_progress(
        self,
        audiobook_id: str,
        chapter_index: int,
        position: float
    ) -> Progress:
        """更新播放进度"""
        try:
            data = {
                "chapter_index": chapter_index,
                "position": position
            }
            
            response = await self.client._put(f"/api/v1/audiobooks/{audiobook_id}/progress", data)
            
            if not response["success"]:
                error = response["error"]
                raise APIError(
                    error["message"],
                    error["code"],
                    error["details"]
                )
            
            data = response["data"]
            data["last_read_at"] = datetime.fromisoformat(data["last_read_at"]) if data.get("last_read_at") else None
            
            return Progress(**data)
            
        except Exception as e:
            if isinstance(e, OfflineLanguagePlayerError):
                raise
            raise APIError(f"更新播放进度失败: {str(e)}")


class StreamPlayer:
    """流媒体播放器"""
    
    def __init__(self, client: 'OfflineLanguagePlayer'):
        self.client = client
        self.ws = None
        self.session_id = None
        self.audio_buffer = []
        self.callbacks = {}
    
    async def connect(self, session_id: str):
        """连接WebSocket流"""
        try:
            ws_url = f"{self.client.ws_url}/api/v1/stream/{session_id}?token={self.client.token}"
            
            self.ws = await websockets.connect(ws_url)
            self.session_id = session_id
            
            # 启动消息监听
            asyncio.create_task(self._listen())
            
        except Exception as e:
            raise APIError(f"WebSocket连接失败: {str(e)}")
    
    async def disconnect(self):
        """断开连接"""
        if self.ws:
            await self.ws.close()
            self.ws = None
            self.session_id = None
    
    async def _listen(self):
        """监听WebSocket消息"""
        try:
            async for message in self.ws:
                data = json.loads(message)
                self._handle_message(data)
        except websockets.exceptions.ConnectionClosed:
            self._handle_message({
                "type": "connection_closed",
                "message": "WebSocket连接已关闭"
            })
        except Exception as e:
            self._handle_message({
                "type": "error",
                "message": f"WebSocket错误: {str(e)}"
            })
    
    def _handle_message(self, data: Dict[str, Any]):
        """处理WebSocket消息"""
        message_type = data.get("type")
        
        if message_type == "audio_chunk":
            self.audio_buffer.append(data["data"])
            if "audio_chunk" in self.callbacks:
                self.callbacks["audio_chunk"](data)
        
        elif message_type == "progress_update":
            if "progress_update" in self.callbacks:
                self.callbacks["progress_update"](data)
        
        elif message_type == "status_change":
            if "status_change" in self.callbacks:
                self.callbacks["status_change"](data)
        
        elif message_type == "error":
            if "error" in self.callbacks:
                self.callbacks["error"](data)
        
        elif message_type == "connection_closed":
            if "connection_closed" in self.callbacks:
                self.callbacks["connection_closed"](data)
    
    def on(self, event: str, callback):
        """注册事件回调"""
        self.callbacks[event] = callback
    
    def get_audio_buffer(self) -> List[str]:
        """获取音频缓冲区"""
        return self.audio_buffer.copy()
    
    def clear_audio_buffer(self):
        """清空音频缓冲区"""
        self.audio_buffer.clear()


class OfflineLanguagePlayer:
    """离线语言播放系统客户端"""
    
    def __init__(
        self,
        base_url: str,
        token: str,
        timeout: int = 30,
        retries: int = 3
    ):
        self.base_url = base_url.rstrip('/')
        self.ws_url = base_url.replace('http://', 'ws://').replace('https://', 'wss://')
        self.token = token
        self.timeout = aiohttp.ClientTimeout(total=timeout)
        self.retries = retries
        
        # 创建HTTP会话
        self.session = aiohttp.ClientSession(
            timeout=self.timeout,
            headers={"Authorization": f"Bearer {token}"}
        )
        
        # 初始化模块
        self.tts = TTSModule(self)
        self.audiobooks = AudioBookModule(self)
    
    async def __aenter__(self):
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.close()
    
    async def close(self):
        """关闭客户端"""
        await self.session.close()
    
    async def _request(
        self,
        method: str,
        endpoint: str,
        data: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """发送HTTP请求"""
        url = f"{self.base_url}{endpoint}"
        
        for attempt in range(self.retries):
            try:
                async with self.session.request(
                    method,
                    url,
                    json=data,
                    params=params
                ) as response:
                    if response.status == 401:
                        raise AuthenticationError("认证失败，请检查token")
                    elif response.status == 429:
                        raise RateLimitError("请求过于频繁，请稍后重试")
                    elif response.status >= 400:
                        error_data = await response.json()
                        error = error_data.get("error", {})
                        raise APIError(
                            error.get("message", f"HTTP {response.status}"),
                            error.get("code"),
                            error.get("details")
                        )
                    
                    return await response.json()
            
            except asyncio.TimeoutError:
                if attempt == self.retries - 1:
                    raise APIError("请求超时")
                continue
            
            except Exception as e:
                if isinstance(e, OfflineLanguagePlayerError):
                    raise
                
                if attempt == self.retries - 1:
                    raise APIError(f"请求失败: {str(e)}")
                
                await asyncio.sleep(1)
    
    async def _get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """GET请求"""
        return await self._request("GET", endpoint, params=params)
    
    async def _post(self, endpoint: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """POST请求"""
        return await self._request("POST", endpoint, data=data)
    
    async def _put(self, endpoint: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """PUT请求"""
        return await self._request("PUT", endpoint, data=data)
    
    async def _delete(self, endpoint: str) -> Dict[str, Any]:
        """DELETE请求"""
        return await self._request("DELETE", endpoint)
    
    async def health_check(self) -> Dict[str, Any]:
        """健康检查"""
        try:
            response = await self._get("/api/v1/health")
            return response["data"]
        except Exception as e:
            raise APIError(f"健康检查失败: {str(e)}")
    
    async def get_stats(self) -> Dict[str, Any]:
        """获取系统统计"""
        try:
            response = await self._get("/api/v1/stats")
            return response["data"]
        except Exception as e:
            raise APIError(f"获取统计失败: {str(e)}")
    
    def create_stream_player(self) -> StreamPlayer:
        """创建流媒体播放器"""
        return StreamPlayer(self)


# 便捷函数
async def create_client(
    base_url: str,
    token: str,
    timeout: int = 30,
    retries: int = 3
) -> OfflineLanguagePlayer:
    """创建客户端实例"""
    return OfflineLanguagePlayer(
        base_url=base_url,
        token=token,
        timeout=timeout,
        retries=retries
    )


# 使用示例
async def main():
    """使用示例"""
    # 创建客户端
    async with create_client(
        base_url="http://localhost:8000",
        token="your_token_here"
    ) as client:
        
        # TTS示例
        print("=== TTS示例 ===")
        result = await client.tts.synthesize(TTSRequest(
            text="你好，这是一个语音合成测试。",
            language="zh-CN"
        ))
        
        if result.success:
            print(f"语音合成成功: {result.audio_url}")
            
            # 下载音频
            if result.audio_url:
                await client.tts.download_audio(result.audio_url, "output.wav")
                print("音频已下载到 output.wav")
        
        # 获取可用语音
        voices = await client.tts.get_voices()
        print(f"可用语音数量: {len(voices)}")
        for voice in voices[:3]:  # 显示前3个
            print(f"- {voice.name} ({voice.language}, {voice.gender})")
        
        # 有声读物示例
        print("\n=== 有声读物示例 ===")
        task = await client.audiobooks.create(AudioBookRequest(
            title="测试有声读物",
            content="这是第一章的内容。\n\n这是第二章的内容。",
            language="zh-CN",
            auto_chapters=True
        ))
        
        print(f"有声读物创建任务: {task['task_id']}")
        
        # 获取有声读物列表
        audiobooks_data = await client.audiobooks.list()
        print(f"有声读物数量: {audiobooks_data['pagination']['total']}")
        
        # 流媒体播放示例
        if audiobooks_data["items"]:
            audiobook = audiobooks_data["items"][0]
            session = await client.audiobooks.play(audiobook.id)
            
            print(f"播放会话: {session.session_id}")
            
            # 创建流媒体播放器
            player = client.create_stream_player()
            
            def on_audio_chunk(data):
                print(f"收到音频块: {data['sequence']}")
            
            def on_progress_update(data):
                print(f"播放进度: {data['position']}")
            
            player.on("audio_chunk", on_audio_chunk)
            player.on("progress_update", on_progress_update)
            
            await player.connect(session.session_id)
            await asyncio.sleep(2)  # 监听2秒
            await player.disconnect()


if __name__ == "__main__":
    # 运行示例
    asyncio.run(main())
